# barco/binreposd.py

import os
import SocketServer
import SimpleXMLRPCServer
import threading
import uuid
import datetime
import dateutil.parser
import xml.parsers.expat

from . import error

class Server(object):

    class ThreadedXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer): pass
        
    class RequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): pass

    class Tag(object):

        def __init__(self, user=None, xml_data=None):
            if (xml_data != None):
                self.user = '-unknown-'
                self.uuid = '-unknown-'
                self.time = datetime.datetime.min
                self.parser = xml.parsers.expat.ParserCreate()
                self.parser.StartElementHandler = self.__xml_start
                self.parser.Parse(xml_data, True)
            else:
                if (user == None): self.user = '-unknown-'
                else: self.user = user
                self.uuid = str(uuid.uuid1())
                self.time = datetime.datetime.now()
                self.parser = None
            return None

        def to_dict(self):
            ret = dict()
            ret['user'] = self.user
            ret['time'] = self.time.isoformat(' ')
            ret['uuid'] = self.uuid
            return ret

        def to_xml(self):
            xml = list()
            xml.append('<?xml version="1.0"?>')
            xml.append('<tag>')
            xml.append('  <user>%s</user>' % self.user)
            xml.append('  <uuid>%s</uuid>' % self.uuid)
            xml.append('  <time>%s</time>' % self.time.isoformat())
            xml.append('</tag>')
            xml.append('')
            return '\n'.join(xml)

        def get_time(self): return self.time
        def get_uuid(self): return self.uuid

        def __xml_start(self, name, attrs):
            if name == 'tag': 
                self.parser.StartElementHandler = self.__xml_tag_start
                self.parser.EndElementHandler = self.__xml_tag_end                
            return None

        def __xml_tag_start(self, name, attrs):
            if name == 'user':   self.parser.CharacterDataHandler = self.__xml_set_user
            elif name == 'uuid': self.parser.CharacterDataHandler = self.__xml_set_uuid
            elif name == 'time': self.parser.CharacterDataHandler = self.__xml_set_time
            else: self.parser.CharacterDataHandler = None
            return None       

        def __xml_tag_end(self, name):
            if name == 'tag':
                self.parser.StartElementHandler = self.__xml_start
                self.parser.EndElementHandler = None
                self.parser.CharacterDataHandler = None
            return None       

        def __xml_set_user(self, data):
            self.user = data
            self.parser.CharacterDataHandler = None
            return None

        def __xml_set_uuid(self, data):
            self.uuid = data
            self.parser.CharacterDataHandler = None
            return None       

        def __xml_set_time(self, data):
            self.time = dateutil.parser.parse(data)
            self.parser.CharacterDataHandler = None
            return None
        
    class Functions(object):

        def __init__(self, server):
            self.server = server
            return None

        def list_tags(self):
            self.server.lock.acquire()
            tags = self.server.tags.values()
            self.server.lock.release()
            tags.sort(key=lambda x: (x.get_time()), reverse=False)
            return map(lambda x: x.to_dict(), tags)

        def create_tag(self, user):
            tag = Server.Tag(user=user)
            self.server.lock.acquire()            
            self.server.tags[tag.get_uuid()] = tag

            tag_directory = os.path.join(self.server.repository, tag.get_uuid())
            os.makedirs(tag_directory)
            
            tag_info = os.path.join(tag_directory, 'tag_info.xml')
            f = open(tag_info, 'w')
            f.write(tag.to_xml())
            f.close()

            self.server.lock.release()            
            return tag.get_uuid()

    def __init__(self):  

        # setup the repository directory
        install_dir = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..'))
        self.repository = os.path.join(install_dir, 'var', 'binrepos')

        # Setting up the xmlrpc server
        self.server = Server.ThreadedXMLRPCServer(('0.0.0.0', 4243), Server.RequestHandler, False)
        self.server.register_instance(Server.Functions(self))
        self.server.allow_reuse_address = True

        # initialize private data
        self.tags = dict()
        
        # initialize mutex
        self.lock = threading.RLock()

        return None

    def set_repository(self, repository):
        self.repository = repository
        return None

    def run(self):
        
        # Chech if our repository is a directory
        if not os.path.isdir(self.repository):
            raise error.Error('specified repository \'%s\' is not a directory.' % self.repository)
        
        self.initialize_repository()
        
        # Start the server
        self.server.serve_forever()
        return None    

    def walk_and_find_tags(self):
        ret = dict()
        for root, dirs, files in os.walk(self.repository):
            for d in dirs:
                try: 
                    f = open(os.path.join(self.repository, d, 'tag_info.xml'), 'r')
                    ret[d] = Server.Tag(xml_data=f.read())
                    f.close()
                except IOError: pass                                      
            del dirs[:]
        return ret

    def initialize_repository(self):
        try:
            self.lock.acquire()
            self.tags = self.walk_and_find_tags()            
            self.lock.release()
        except:
            self.lock.release()
            raise           
            
        return None
